/*
 * Copyright (C) 2009 Dimitrios C. Michalakos
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.cs.util.db.representation;

import com.cs.util.binary.Base16;
import com.cs.util.db.Database;
import com.cs.util.db.ioc.IOController;
import com.cs.util.db.ioc.IOCException;
import java.io.IOException;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.mail.BodyPart;
import javax.mail.MessagingException;

/**
 * XhtmlForm can be used for generating a XHTML Form document.
 * @author dimitris@jmike.gr
 */
public class XhtmlForm {

    private Database db;
    private ResultSet rs;
    private IOController[] ioc;
    private boolean isEmpty;
    private String title = "Another Xhtml Form Respresentation";//set default title
    private String cssLink = null;
    private String formAction = "";//set default formAction as "" (empty means this)

//    private int errorColumn = -1;
//    private String error = null;
//    private MultiValuedMap<String, String> formData = null;
    /**
     * Parses the designated resultset and construncts a new XHTML Form document.
     * @param db the database that the resultset derives from.
     * @param rs the resultset upon which the document will be built.
     * @throws SQLException
     */
    public XhtmlForm(Database db, ResultSet rs) throws SQLException {
        this.db = db;
        this.rs = rs;
        this.ioc = db.getIOControllerArray(rs);
        rs.beforeFirst();
        this.isEmpty = !rs.next();
    }

    /**
     * Parses the designated table and columns and construncts a new XHTML Form document.
     * @param db the database that the resultset derives from.
     * @param table the table upon which the document will be built.
     * @param columns the columns upon which the document will be built.
     * @throws SQLException
     */
    public XhtmlForm(Database db, String table, String[] columns) throws SQLException {
        this.db = db;
        this.rs = null;
        this.ioc = db.getIOControllerArray(table, columns);
        this.isEmpty = true;
    }

    /**
     * Sets an input lock for the designated io controller of this representation.
     * When an input lock is set, the controller cannot except different values.
     * @param id the id of the io controller that the lock will be set to.
     * @param lockValue the value of the input lock.
     * @throws IOCException
     */
    public void setLock(String id, String lockValue) throws IOCException {
        for (int i = 0; i < ioc.length; i++) {//iterate io controllers
            if (ioc[i].getId().equalsIgnoreCase(id)) {
                ioc[i].setInputLock(lockValue);
                break;
            }
        }
    }

    /**
     * Sets an input lock for the designated io controller of this representation.
     * When an input lock is set, the controller cannot except different values.
     * @param table the table of the io controller that the lock will be set to.
     * @param column the column of the io controller that the lock will be set to.
     * @param lockValue the value of the input lock.
     * @throws IOCException
     */
    public void setLock(String table, String column, String lockValue) throws IOCException {
        setLock(table + "." + column, lockValue);
    }

//    /**
//     * Parses the designated multipart form data and returns a HashMap<String, String>.
//     * The keys of the returned map are the qualified names of the form's input fields.
//     * The values of the returned map are the submitted values for each key.
//     * @param form a submitted form encoded encoded in multipart/form-data.
//     * @return
//     */
//    public Map<String, String> parse(MimeMultipart form) {
//        Map<String, String> map = null;
//        /* fill the map */
//        if (form != null) {//form is not null
//            try {
//                map = new HashMap<String, String>(form.getCount());
//                for (int i = 0; i < form.getCount(); i++) {//iterate form body parts
//                    final BodyPart bodyPart = form.getBodyPart(i);//get current bodyPart
//                    final String k = getBodyPartName(bodyPart);//get key
//                    final String v = getBodyPartValue(bodyPart);//get value
//                    map.put(k, v);
//                }
//            } catch (MessagingException ex) {
//                /* ignore the error */
//            } catch (IOException ex) {
//                /* ignore the error */
//            }
//        }
//        /* validate the map */
//        for (int i = 0; i < ioc.length; i++) {//iterate io controllers
//            if (map.containsKey(ioc[i].getId())) {//the id of the current io controller is contained in this map
//                final String v = map.get(ioc[i].getId());//get the value
//                try {
//                    map.put(ioc[i].getId(), ioc[i].validate(v));
//                } catch (IOCException ex) {//value cannot be validated
//                    map.remove(ioc[i].getId());
//                }
//            }
//        }
//        /* return the map */
//        return map;
//    }

    /**
     * Closes the internal resultset of this representation and clears meta data resources from memory.
     * @throws java.sql.SQLException
     */
    public void close() throws SQLException {
        db = null;
        if (rs != null) {
            rs.close();
        }
        ioc = null;
        isEmpty = false;
        title = "Another Xhtml Form Respresentation";
        cssLink = null;
        formAction = "";
    }

    /**
     * Sets the title of the Xhtml document.
     * @param title the title.
     * Title can be used for:
     * Defining a title in the browser toolbar.
     * Providing a title for the page when it is added to favorites.
     * Displaying a title for the page in search-engine results.
     * @return true if title has been set, false if not.
     */
    public boolean setTitle(String title) {
        if (title != null) {
            this.title = title;
            return true;
        } else {
            return false;
        }
    }

    /**
     * Sets the location of a linked stylesheet for the Xhtml document.
     * @param url the url of the css file.
     * @return true if stylesheet URL has been set, false if not.
     */
    public boolean setStylesheetURL(String url) {
        this.cssLink = url;
        return true;
    }

    /**
     * Sets the action attribute of the form.
     * Action is used to specify where to send the form-data when the form is submitted.
     * @param url the url that the data will be send to.
     * @return true if form action has been set, false if not.
     */
    public boolean setFormAction(String url) {
        if (url != null) {
            this.formAction = url;
            return true;
        } else {
            return false;
        }
    }

//    /**
//     * Sets an error for this form.
//     * @param error
//     */
//    public void setError(IOCException error) {
//        for (int i = 1; i <= rsmi.getColumnCount(); i++) {//iterate columns
//            try {
//                if (rsmi.getTable(i).equals("") && rsmi.getColumn(i).equals("")) {
//                    this.errorColumn = -1;
//                    this.error = null;
//                }
//            } catch (SQLException ex) {
//                /* ignore the error */
//            }
//        }
//    }

//    /**
//     * Sets the value of the input elements ot this form.
//     * @param formData
//     */
//    public void setFormData(MultiValuedMap<String, String> formData) {
//        this.formData = formData;
//    }
    /**
     * Returns this Representation as a String.
     * @return
     * @throws SQLException
     */
    public String display() throws SQLException {
        StringBuilder s = new StringBuilder();
        s.append("<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">");
        s.append("<html xmlns=\"http://www.w3.org/1999/xhtml\">");
        s.append("<head>");
        s.append("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />");
        if (cssLink != null) {
            s.append("<link rel=\"stylesheet\" href=\"" + cssLink + "\" type=\"text/css\" />");
        }
        s.append("<title>" + title + "</title>");
        s.append("</head>");
        s.append("<body>");
        s.append("<form action=\"" + formAction + "\" method=\"post\" enctype=\"multipart/form-data\">");
        for (int i = 0; i < ioc.length; i++) {//iterate io controllers
            s.append("<div>");
            s.append("<label for=\"" + ioc[i].getId() + "\">" + ioc[i].getLabel() + "</label>");
            s.append(ioc[i].getXhtmlFormElement(rs, isEmpty));
//            if (i == errorColumn) {
//                s.append("<span class=\"error\">");
//                s.append(error);
//                s.append("</span>");
//            }
            s.append("</div>");
        }
//        s.append("<div>");
//        s.append("<input type=\"file\" name=\"fileman\" />");
//        s.append("</div>");
        s.append("<div>");
        s.append("<button type=\"submit\">Submit</button>");
        s.append("<button type=\"reset\">Reset</button>");
        s.append("</div>");
        s.append("</form>");
        s.append("</body>");
        s.append("</html>");
        return s.toString();
    }

    /**
     * Returns the name of the designated BodyPart.
     * @param bodyPart
     * @return
     * @throws javax.mail.MessagingException
     */
    private static String getBodyPartName(BodyPart bodyPart) throws MessagingException {
        if (bodyPart.getDisposition().equals("form-data")) {
            final String[] h = bodyPart.getHeader("Content-Disposition");
            if (h != null && h.length > 0) {
                final int a = h[0].indexOf("\"") + 1;
                final int b = h[0].indexOf("\"", a);
                return h[0].substring(a, b);
            }
        }
        return "";
    }

    /**
     * Returns the value of the designated BodyPart.
     * @param bodyPart
     * @return
     * @throws javax.mail.MessagingException
     * @throws java.io.IOException
     */
    private static String getBodyPartValue(BodyPart bodyPart) throws MessagingException, IOException {
        if (bodyPart.getContentType().equals("text/plain")) {
            return (String) bodyPart.getContent();
        } else {
            return Base16.toHexString(bodyPart.getInputStream().toString().getBytes());
        }
    }
}
